#pragma once
#include <iostream>
#include <unistd.h>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>

#include "log.hpp"
#include "err.hpp"
const static int defaultSock = -1; // 套接字初始化值
const static int backlog = 32;     // 监听套接字，等待的套接字数量
class Socket
{
public:
    Socket() : _sock(defaultSock)
    {
    }
    ~Socket() // 关闭前要检测是否已经关闭
    {
        if (_sock != defaultSock)
            close(_sock);
    }

public:
    // 创建套接字
    void InitSocket()
    {
        // 创建
        _sock = socket(AF_INET, SOCK_STREAM, 0);
        if (_sock < 0)
        {
            logMessage(FATAL, "create socket err");
            exit(SOCKET_ERR);
        }
        logMessage(NORMAL, "creat socket success:%d", _sock);

        // 设置套接字属性
        // 地址重用与相同地址（新套接字无需等待tcp挥手的最后一步time_wait） 和 地址端口可重复绑定（同一链接获取消息时会给所有bind其的套接字消息）
        int opt = 1;
        setsockopt(_sock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
    }

    // 绑定套接字
    void Bind(int port, std::string ip = "")
    {
        in_addr_t IP;
        if (ip.empty())
            IP = INADDR_ANY;
        else
            IP = inet_addr(ip.c_str());

        // 绑定的网络信息
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = IP;
        if (bind(_sock, (sockaddr *)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "bind socket err");
            exit(BIND_ERR);
        }
        logMessage(NORMAL, "bind socket success: %d", _sock);
    }

    // 将套接字设为监听状态
    void Listen()
    {
        // 设置socket 为监听状态
        if (listen(_sock, backlog) < 0)
        {
            logMessage(FATAL, "listen socket error");
            exit(LISTEN_ERR);
        }
        logMessage(NORMAL, "listen socket success");
    }

    // 获取
    int Accept(std::string *clientIp, uint16_t *clientPort)
    {
        struct sockaddr_in peer;
        socklen_t sockLen = sizeof(peer);
        int sock = accept(_sock, (sockaddr *)&peer, &sockLen);
        if (sock < 0)
        {
            logMessage(ERROR, "accept a new sock err");
            return defaultSock;
        }

        *clientIp = inet_ntoa(peer.sin_addr);
        *clientPort = ntohs(peer.sin_port);
        return sock;
    }

    //获取套接字
    int GetSock()
    {
        return _sock;
    }
    //关闭
    void Close()
    {
        if(_sock != defaultSock) close(_sock);
    }
private:
    int _sock; // 将兼容listen套接字，并不一定是listen套接字
};